#ifndef UTILITY_THREADLOCALSINGLETON_H
#define UTILITY_THREADLOCALSINGLETON_H

#include "Noncopyable.h"
#include "assertion.h"
#include "feature.h"
#include "wrap/pthread.h"

template<typename T>
class ThreadLocalSingleton final {
public:
    static T &INSTANCE();

    ThreadLocalSingleton() = delete;
    ~ThreadLocalSingleton() = default;

private:
    NONCOPYABLE(ThreadLocalSingleton);

    class _Deleter {
    public:
        _Deleter(): _key{0} {
            Wrap::pthread_key_create_(&_key, ThreadLocalSingleton::_DESTRUCTOR);
        }
        ~_Deleter() {
            Wrap::pthread_key_delete_(_key);
        }

        void set(const void *keyNew) {
            assert(!Wrap::pthread_getspecific_(_key));
            Wrap::pthread_setspecific_(_key, keyNew);
        }
    private:
        NONCOPYABLE(_Deleter);

        pthread_key_t _key;
    };

    static void _DESTRUCTOR(MAYBE_UNUSED void *x);

    inline static thread_local T *_T = nullptr;
    inline static _Deleter _DELETER{};
};

template<typename T>
T &ThreadLocalSingleton<T>::INSTANCE() {
    if (!_T) {
        _T = new T;
        _DELETER.set(_T);
    }
    return *_T;
}

template<typename T>
void ThreadLocalSingleton<T>::_DESTRUCTOR(MAYBE_UNUSED void *x) {
    static_assert(sizeof(T), "T must be complete type");
    assert(x == _T);
    delete _T;
    _T = nullptr;
}


#endif